package com.crazypos.service.impl;

import cn.hutool.core.collection.CollectionUtil;
import com.alibaba.fastjson.JSON;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.crazypos.common.utils.RedisUtil;
import com.crazypos.common.utils.SecurityUtil;
import com.crazypos.constant.CommonConstant;
import com.crazypos.dao.*;
import com.crazypos.enums.CanDelete;
import com.crazypos.exceptions.MyException;
import com.crazypos.pojo.*;
import com.crazypos.pojo.bo.CreateRoleBO;
import com.crazypos.pojo.bo.UpdateRoleBO;
import com.crazypos.pojo.vo.RoleAndPermissionVO;
import com.crazypos.pojo.vo.RoleDetailVO;
import com.crazypos.pojo.vo.RoleMenuVO;
import com.crazypos.pojo.vo.UserRoleVO;
import com.crazypos.service.IRoleService;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.ObjectUtils;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

@Service
@Slf4j
public class RoleService extends ServiceImpl<RoleDao, RoleEntity> implements IRoleService {

    @Autowired
    private RoleDao roleDao;

    @Autowired
    private RolePermissionDao rolePermissionDao;

    @Autowired
    private PermissionDao permissionDao;

    @Autowired
    private UserRoleDao userRoleDao;

    @Autowired
    private RoleMenuDao roleMenuDao;

    @Autowired
    private MenuDao menuDao;

    @Autowired
    private RedisUtil redisUtil;

    @Override
    @Transactional
    public List<RoleAndPermissionVO> initGetAllRolesAndPermissions() {
        List<RoleAndPermissionVO> resultList = new ArrayList<>();
        List<RoleEntity> roleList = roleDao.selectList(null);
        for (RoleEntity role :
                roleList) {
            RoleAndPermissionVO vo = new RoleAndPermissionVO();
            BeanUtils.copyProperties(role, vo);

            List<PermissionEntity> permissionEntities = permissionDao.getPermissionsByRoleId(role.getId());

            if (permissionEntities != null && permissionEntities.size() > 0) {
                for (PermissionEntity entity :
                        permissionEntities) {
                    vo.getPermissionsList().add(entity);
                }
            }

            resultList.add(vo);
        }
        return resultList;
    }

    @Override
    public UserRoleVO getRoleByUserId(String userId) {
        return userRoleDao.getUserRole(userId);
    }

    @Override
    public List<String> getPermissionByRoleId(String roleId, String organizationId) {

        // 将新建的ROLE存储到REDIS
        String key = SecurityUtil.createPermissionKey(organizationId, roleId);
        List<String> permissions = null;
        try {
            permissions = JSON.parseArray(redisUtil.hget(key, CommonConstant.PERMISSION_FIELDS_NAME), String.class);
        } catch (Exception ex) {
            log.error("find role permissions is fail, roleId is : [{}], organizationId is : [{}]", roleId, organizationId);
        }

        if (permissions == null) {
            permissions = permissionDao.getPermissionsByRoleId(roleId)
                    .stream().map(item -> item.getValue()).collect(Collectors.toList());
            redisUtil.hset(key, CommonConstant.PERMISSION_FIELDS_NAME, JSON.toJSONString(permissions));
        }

        return permissions;
    }

    @Override
    public IPage<RoleEntity> getRolesList(Integer startPage, Integer pageSize, String name, String organizationId) {
        if (startPage == null || startPage <= 0) {
            startPage = 1;
        }
        if (pageSize == null || pageSize <= 0) {
            pageSize = 10;
        }

        QueryWrapper<RoleEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("organization_id", organizationId);
        if (!StringUtils.isBlank(name)) {
            queryWrapper.like("name", name.trim());
        }

        Page<RoleEntity> page = new Page<>(startPage, pageSize);


        return roleDao.selectPage(page, queryWrapper);
    }

    @Override
    @Transactional
    public void createRole(CreateRoleBO bo, String userId, String organizationId) {
        RoleEntity roleEntity = new RoleEntity();
        roleEntity.setName(bo.getName());
        roleEntity.setOrganizationId(organizationId);
        roleEntity.setCreatedBy(userId);
        roleEntity.setCanDelete(CanDelete.YES.getValue());

        roleDao.insert(roleEntity);

        insertRoleMenu(roleEntity.getId(), bo.getMenusIds(), 1);
        insertRoleMenu(roleEntity.getId(), bo.getRoleMenuIds(), 2);

        String permissionIds = bo.getPermissionsIds();
        insertRolePermission(roleEntity.getId(), permissionIds);
        // 将新建的ROLE存储到REDIS
        updateRedis(roleEntity.getId(), organizationId, permissionIds, bo.getMenusIds(), bo.getRoleMenuIds());
    }

    @Override
    @Transactional
    public void deleteRole(String roleId, String organizationId) {
        QueryWrapper<RoleEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", roleId).eq("organization_id", organizationId);
        RoleEntity exists = roleDao.selectOne(queryWrapper);
        if (exists == null) {
            throw new MyException(404, "角色不存在");
        }

        if(exists.getCanDelete() == CanDelete.NO.getValue()){
            throw new MyException(500, "该角色不允许删除");
        }

        roleDao.deleteById(roleId);
        rolePermissionDao.delete(
                new QueryWrapper<RolePermissionEntity>()
                        .eq("role_id", roleId)
        );

        userRoleDao.delete(
                new QueryWrapper<UserRoleEntity>()
                        .eq("role_id", roleId)
        );

        roleMenuDao.delete(
                new QueryWrapper<RoleMenuEntity>()
                        .eq("role_id", roleId)
        );

        // 删除完成之后更新到redis中
        String redisKey = SecurityUtil.createPermissionKey(organizationId, roleId);
        redisUtil.del(redisKey);
    }

    @Transactional
    public void updateRole(UpdateRoleBO bo, String userId, String organizationId) {
        QueryWrapper<RoleEntity> queryWrapper = new QueryWrapper<>();
        queryWrapper.eq("id", bo.getRoleId())
                .eq("organization_id", organizationId);

        RoleEntity roleEntity = roleDao.selectOne(queryWrapper);

        if (ObjectUtils.isEmpty(roleEntity)) {
            throw new MyException(404, "角色不存在");
        }

        roleEntity.setName(bo.getName());

        roleDao.updateById(roleEntity);

        rolePermissionDao.delete(
                new QueryWrapper<RolePermissionEntity>().eq("role_id", bo.getRoleId())
        );

        roleMenuDao.delete(
                new QueryWrapper<RoleMenuEntity>()
                        .eq("role_id", bo.getRoleId())
        );

        String menusIds = bo.getMenusIds();
        insertRoleMenu(roleEntity.getId(), menusIds, 1);
        insertRoleMenu(roleEntity.getId(), bo.getRoleMenuIds(), 2);

        String permissionIds = bo.getPermissionsIds();
        insertRolePermission(bo.getRoleId(), permissionIds);

        // 将更新的ROLE信息存储到REDIS
        updateRedis(roleEntity.getId(), organizationId, permissionIds, bo.getMenusIds(), bo.getRoleMenuIds());

    }

    @Override
    public List<RoleMenuVO> getRoleMenu(String roleId, Integer menuType, String organizationId) {

        String filedName = CommonConstant.NAV_MENUS_NAME;

        if (menuType == null || menuType < 1 || menuType > 2) {
            menuType = 1;
        }

        if (menuType == 2) {
            filedName = CommonConstant.ROLE_MENUS_NAME;
        }

        // 将新建的ROLE存储到REDIS
        String key = SecurityUtil.createPermissionKey(organizationId, roleId);

        List<RoleMenuVO> roleMenuVOS = null;
        try {
            roleMenuVOS = JSON.parseArray(redisUtil.hget(key, filedName), RoleMenuVO.class);
        } catch (Exception ex) {
            log.error("find role menus fail, roleId is : [{}], organizationId is : [{}]. menuType is : [{}]", roleId, organizationId, menuType);
        }

        if (CollectionUtil.isEmpty(roleMenuVOS)) {
            roleMenuVOS = roleMenuDao.getRoleMenus(roleId, menuType);
            redisUtil.hset(key, filedName, JSON.toJSONString(roleMenuVOS));
        }

        return roleMenuDao.getRoleMenus(roleId, menuType);
    }

    @Override
    public IPage<RoleAndPermissionVO> getRolesWithPermissions(Integer startPage, Integer pageSize, String name, String organizationId) {
        if (startPage == null || startPage <= 0) {
            startPage = 1;
        }
        if (pageSize == null || pageSize <= 0) {
            pageSize = 10;
        }

        Page<RoleEntity> page = new Page<>(startPage, pageSize);
        return roleDao.selectRoleWithPermissions(page, organizationId, name);
    }

    @Override
    public RoleDetailVO getRoleByRoleId(String organizationId, String roleId) {
        return roleDao.selectRoleDetail(organizationId, roleId);
    }

    @Transactional
    public void insertRolePermission(String roleId, String permissionIds) {
        if (!StringUtils.isBlank(permissionIds)) {
            List<String> permissionIdList = Arrays.asList(permissionIds.split(","));
            if (permissionIdList.size() > 0) {
                for (String permissionId :
                        permissionIdList) {
                    RolePermissionEntity rolePermissionEntity = new RolePermissionEntity();

                    rolePermissionEntity.setRoleId(roleId);
                    rolePermissionEntity.setPermissionId(permissionId);
                    rolePermissionDao.insert(rolePermissionEntity);
                }
            }
        }
    }

    @Transactional
    public void insertRoleMenu(String roleId, String menusIds, int menuType) {
        if (!StringUtils.isBlank(menusIds)) {
            List<String> menusIdList = Arrays.asList(menusIds.split(","));
            if (menusIdList.size() > 0) {
                for (String menuId :
                        menusIdList) {
                    RoleMenuEntity roleMenuEntity = new RoleMenuEntity();
                    roleMenuEntity.setRoleId(roleId);
                    roleMenuEntity.setMenuId(Integer.valueOf(menuId));
                    roleMenuEntity.setType(menuType);
                    roleMenuDao.insert(roleMenuEntity);
                }
            }
        }
    }

    private void updateRedis(String roleId, String organizationId, String permissionsIds, String getMenusIds, String roleMenusId) {
        if (StringUtils.isBlank(roleId) || StringUtils.isBlank(organizationId)) {
            return;
        }
        // 将新建的ROLE存储到REDIS
        String key = SecurityUtil.createPermissionKey(organizationId, roleId);

        // 查找权限value
        List<String> permissionValueList = permissionDao.selectList(
                        new QueryWrapper<PermissionEntity>()
                                .in("id", permissionsIds.split(","))
                )
                .stream().map(item -> item.getValue()).collect(Collectors.toList());

        // 查找侧边栏菜单
        List<RoleMenuVO> roleMenuVOS = menuDao.selectList(
                        new QueryWrapper<MenuEntity>()
                                .in("id", getMenusIds.split(","))
                )
                .stream()
                .map(RoleMenuVO::new)
                .collect(Collectors.toList());

        // 查找权限菜单
        List<RoleMenuVO> roleMenuVOS2 = menuDao.selectList(
                        new QueryWrapper<MenuEntity>()
                                .in("id", roleMenusId.split(","))
                )
                .stream()
                .map(RoleMenuVO::new)
                .collect(Collectors.toList());

        redisUtil.hset(key, CommonConstant.PERMISSION_FIELDS_NAME, JSON.toJSONString(permissionValueList));
        redisUtil.hset(key, CommonConstant.NAV_MENUS_NAME, JSON.toJSONString(roleMenuVOS));
        redisUtil.hset(key, CommonConstant.ROLE_MENUS_NAME, JSON.toJSONString(roleMenuVOS2));
    }
}
